/*
	BlockQueue class
	Written by next generations
	Version 1.0

	a cycle-queue management class
*/
// changed by, for internal realizations [2/24/2017 Administrator]
#ifndef _BLOCK_QUEUE_H_
#define _BLOCK_QUEUE_H_ 1

#include <cstring>


class BlockQueue
{
public:
	BlockQueue(){
		m_pBlocks = m_pTempBlocks = NULL;
		m_pBookmarks = NULL;
	};
	~BlockQueue(){
		Reset();
	};

	bool Init(int nBlockSize, int nTotalBlock, int nBookmarks=7){
		if (nBlockSize <= 0 || nTotalBlock <= 0 || nBookmarks < 0)
			return false;
		Reset();
		m_pBlocks = new char[nBlockSize*nTotalBlock];
		if (!m_pBlocks)
			return false;
		m_nBlockSize = nBlockSize;
		m_nTotalBlock = nTotalBlock;
		m_pTempBlocks = new char[nBlockSize*nTotalBlock/2+1];
		if (!m_pTempBlocks) {
			Reset();
			return false;
		}
		if (nBookmarks) {
			m_pBookmarks = new int[nBookmarks];
			if (!m_pBookmarks) {
				Reset();
				return false;
			}
		}
		m_nBookmarkNum = nBookmarks;
		Clear();
		return true;
	};

	void Reset(){
		Clear();
		if (m_pBlocks) {
			delete[] m_pBlocks;
			m_pBlocks = NULL;
		}
		if (m_pTempBlocks) {
			delete[] m_pTempBlocks;
			m_pTempBlocks = NULL;
		}
		if (m_pBookmarks) {
			delete[] m_pBookmarks;
			m_pBookmarks = NULL;
		}
	};

	void Clear(){
		m_nHeadIndex = m_nTailIndex = 0;
		m_bIsFull = false;
		ClearAllBookmarks();
	};


	bool PushBack(char *pBlock){
		if (!m_pBlocks || !pBlock)
			return false;
		//one time copy fixed size data, means m_nBlockSize
		memcpy(m_pBlocks+m_nBlockSize*m_nTailIndex, pBlock, m_nBlockSize);
		m_nTailIndex ++;
		if (m_nTailIndex == m_nTotalBlock)
			m_nTailIndex = 0;
		if (m_bIsFull) {
			// PopFront
			m_nHeadIndex ++;
			if (m_nHeadIndex == m_nTotalBlock)
				m_nHeadIndex = 0;
			DecreaseBookmarks();
		}
		else if (m_nTailIndex == m_nHeadIndex)
			m_bIsFull = true;
		return true;
	};

	char *PopFront(){
		if (!m_pBlocks || !GetBlockNum())
			return NULL;
		char	*pRet = m_pBlocks + m_nBlockSize*m_nHeadIndex;
		m_nHeadIndex ++;
		if (m_nHeadIndex == m_nTotalBlock)
			m_nHeadIndex = 0;
		if (m_bIsFull)
			m_bIsFull = false;
		if (m_nHeadIndex == m_nTailIndex)
			m_nHeadIndex = m_nTailIndex = 0;
		DecreaseBookmarks();
		return pRet;
	};

	char *GetFront(){
		if (!m_pBlocks || !GetBlockNum())
			return NULL;
		char	*pRet = m_pBlocks + m_nBlockSize*m_nHeadIndex;
		return pRet;
	};//leiwang.add@	20070615


	int GetBlockNum(){
		if (!m_pBlocks)
			return 0;
		if (m_nHeadIndex < m_nTailIndex)
			return m_nTailIndex - m_nHeadIndex;
		else if (m_nHeadIndex > m_nTailIndex)
			return m_nTotalBlock - (m_nHeadIndex-m_nTailIndex);
		if (m_bIsFull)
			return m_nTotalBlock;
		else
			return 0;
	};
	char *GetBlock(int nIndex){
		if (m_pBlocks)
			if (nIndex >= 0 && nIndex < GetBlockNum()) {
				nIndex += m_nHeadIndex;
				if (nIndex >= m_nTotalBlock)//is equal to nIndex % m_nTotalBlock
					nIndex -= m_nTotalBlock;
				return m_pBlocks+m_nBlockSize*nIndex;
			}
		return NULL;
	};
	void Sequence(){
		if (!GetBlockNum() || m_nHeadIndex < m_nTailIndex)
			return;
		int	nFrontSize = m_nTailIndex * m_nBlockSize;
		int	nEndSize = (m_nTotalBlock-m_nHeadIndex) * m_nBlockSize;
		if (nFrontSize < nEndSize) {
			memcpy(m_pTempBlocks, m_pBlocks, nFrontSize);
			memmove(m_pBlocks, m_pBlocks+m_nHeadIndex*m_nBlockSize, nEndSize);
			memcpy(m_pBlocks+nEndSize, m_pTempBlocks, nFrontSize);
		}
		else {
			memcpy(m_pTempBlocks, m_pBlocks+m_nHeadIndex*m_nBlockSize, nEndSize);
			memmove(m_pBlocks+nEndSize, m_pBlocks, nFrontSize);
			memcpy(m_pBlocks, m_pTempBlocks, nEndSize);
		}
		m_nTailIndex = GetBlockNum();
		if (m_bIsFull)
			m_nTailIndex = 0;
		m_nHeadIndex = 0;
	};
	bool IsFull() { return m_bIsFull; }
	bool IsTobeFull() { return (GetBlockNum() / (float)m_nTotalBlock) > 0.8f; }
	int GetBlockSize() { return m_nBlockSize; }
	int GetQueueCapacity() { return m_nTotalBlock; }


	bool SetBookmark(int nMark, int nIndex){
		if (m_pBookmarks)
			if (nMark >= 0 && nMark < m_nBookmarkNum)
				if (nIndex >= 0 && nIndex < GetBlockNum()) {
					m_pBookmarks[nMark] = nIndex;
					return true;
				}
		return false;
	};
	int GetBookmark(int nMark){
		if (m_pBookmarks)
			if (nMark >= 0 && nMark < m_nBookmarkNum)
				return m_pBookmarks[nMark];
		return -1;
	};
	void ClearBookmark(int nMark){
		if (m_pBookmarks)
			if (nMark >= 0 && nMark < m_nBookmarkNum)
				m_pBookmarks[nMark] = -1;
	};
	void ClearAllBookmarks(){
		if (m_pBookmarks)
			for (int i=0; i<m_nBookmarkNum; i++)
				m_pBookmarks[i] = -1;
	};

private:
	char	*m_pBlocks;
	int		m_nBlockSize;
	int		m_nTotalBlock;
	char	*m_pTempBlocks;

	int		m_nHeadIndex, m_nTailIndex;
	bool	m_bIsFull;

	int		*m_pBookmarks;
	int		m_nBookmarkNum;

	void DecreaseBookmarks(){
		if (m_pBookmarks)
			for (int i=0; i<m_nBookmarkNum; i++)
				if (m_pBookmarks[i] >= 0)
					m_pBookmarks[i] --;
	};

};



#endif
